package gov.fnal.xpath2ldap;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;

import org.w3c.xqparser.XQueryToXQueryX;
import org.w3c.xqparser.XPath;
import org.w3c.xqparser.SimpleNode;
import org.xml.sax.SAXException;
import org.w3c.dom.Document;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;

import java.util.List;
import java.util.ArrayList;
import java.util.Stack;
import java.util.ListIterator;
import java.io.*;

public class XqueryxStaxParser {

    public static String SLASH="/";
    public static String SLASHSLASH="//";
    public static String TWODOTS="..";
    public static String TWOCOLONS="::";
    public static String AMPERSAND="@";
    public static String LEFT_BRACKET="[";
    public static String RIGHT_BRACKET="]";
    public static String LEFT_PAREN="(";
    public static String RIGHT_PAREN=")";
    public static String COMMA=", ";
    public static String AND=" and ";
    public static String OR=" or ";
    public static String EQUALS="=";
    public static String NOT_EQUALS="!=";
    public static String GT=">";
    public static String GTE=">=";
    public static String LT="<";
    public static String LTE="<=";
    public static String PLUS="+";
    public static String MINUS="-";
    public static String MULT="*";
    public static String DIV=" div ";
    public static String MOD=" mod ";
    public static String LAST="last()";
    public static String NODE="node()";
    public static String TEXT="text()";
    public static String COMMENT="comment()";
    public static String UNION="|";

    public static String CHILD="child";
    public static String ATTRIBUTE="attribute";
    public static String ANCESTOR="ancestor";
    public static String ANCESTOR_OR_SELF="ancestor-or-self";
    public static String DESCENDANT="descendant";
    public static String DESCENDANT_OR_SELF="descendant-or-self";
    public static String FOLLOWING="following";
    public static String FOLLOWING_SIBLING="following-sibling";
    public static String NAMESPACE="namespace";
    public static String PARENT="parent";
    public static String PRECEDING="preceding";
    public static String PRECEDING_SIBLING="preceding-sibling";
    public static String SELF="self";

    public static void main (String[] args) {
        //String expr = "//GlueSE[GlueSEAccessProtocol/@Status='up' and (Extra/@cost=5 or @valid='no')]/GlueSA[@GlueSAFreeOnlineSize>0]";
        //String expr ="//GlueSE[GlueSEControlProtocol[@GlueSEControlProtocolVersion='1.1.0'] or GlueSEControlProtocol[@GlueSEControlProtocolVersion='2.2.0']]/GlueSEControlProtocol/@GlueSEControlProtocolEndpoint";
        XqueryxStaxParser runparser = new XqueryxStaxParser();
        runparser.process(args);


    }

    public void process(String[] args) {
        int numberArgsLeft = args.length;
        int argsStart = 0;
        while (numberArgsLeft > 0) {
            if ("-xpathcatalog".equalsIgnoreCase(args[argsStart])) {
                argsStart++;
                numberArgsLeft--;
                String catalogFileName = args[argsStart];
                argsStart++;
                numberArgsLeft--;
                try {
                    processXpathTestCatalog(catalogFileName);
                } catch(Exception e) {
                    System.err.println("catalog process Exception");
                }
            } else {
                String expr = args[argsStart];
                argsStart++;
                numberArgsLeft--;
                System.out.println(expr);
                XqueryxStaxParser runparser = new XqueryxStaxParser();
                try {
                    String xqueryx_str = runparser.parseXpathToXqueryX(expr);
                    List<String> lpathList = runparser.parseXqueryX(xqueryx_str);
                    List<Pair<String, Boolean>> combined = runparser.combineOperations(lpathList);
                    List<String> boolcomb = runparser.combineBooleans(combined);
                    System.out.println("Combined paths are ");
                    for(String fullpath : boolcomb) {
                        System.out.println(fullpath);
                    }
                    List<String> lFullPaths = runparser.getFullPaths(lpathList);
                    System.out.println("Result is");
                    for(String fullpath : lFullPaths) {
                        System.out.println(fullpath);
                    }
                } catch(Exception e) {
                    System.err.println("parse Exception");
                }
            }


        }
    }
    private void processXpathTestCatalog(String catalogFileName)
            throws ParserConfigurationException, SAXException, IOException {
        System.out.println("Running catalog for: " + catalogFileName);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.parse(new File(catalogFileName));
        //InputSource source = new InputSource(catalogFileName);
        //Document doc = db.parse( source );

        NodeIterator testCases = ((DocumentTraversal) doc).createNodeIterator(
                doc, NodeFilter.SHOW_ELEMENT, new NodeFilter() {
            public short acceptNode(org.w3c.dom.Node node) {
                String nm = node.getNodeName();
                return (nm.equals("test")) ? NodeFilter.FILTER_ACCEPT
                        : NodeFilter.FILTER_SKIP;
            }
        }, true);
        org.w3c.dom.Element testCase;
        XqueryxStaxParser runparser = new XqueryxStaxParser();
        while (((testCase = (org.w3c.dom.Element) testCases.nextNode()) != null)) {
            String expr = testCase.getAttribute("value");
            String xqueryx_str = runparser.parseXpathToXqueryX(expr);
            System.out.println(xqueryx_str);
            System.out.println(expr);
            List<String> lpathList=null;
            try {
                lpathList = runparser.parseXqueryX(xqueryx_str);
            } catch (XMLStreamException xse) {
                System.err.println("XMLStreamException");
            }
            //System.out.println("Paths are ");
            //        for(String path : lpathList) {
            //            System.out.println(path);
            //}
        }
    }

    public String parseXpathToXqueryX(String expr) {
        String xqueryx_str=null;
        XPath parser = new XPath(new StringReader(expr));
        SimpleNode tree=null;
        try {
            tree = parser.XPath2();
        } catch (Exception e) {
            System.err.println("parser.XPath2 Exception");
            e.printStackTrace();
        }
        if (null == tree)
            System.err.println("Error!");
        else {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(62*1024);
            PrintStream ps = new PrintStream(baos);
            try {
                XQueryToXQueryX trans = new XQueryToXQueryX();
                trans.transformNoEncodingException(tree, null, ps);
            } catch (Exception e) {
                System.err.println("transform Exception");
            }
            xqueryx_str = new String(baos.toByteArray());
        }

        return xqueryx_str;
    }

    public List<String> parseXqueryX(String xqueryx_str) throws XMLStreamException {
        //System.out.println(xqueryx_str);
        XMLInputFactory factory = XMLInputFactory.newInstance();
        //XMLStreamReader parser = factory.createXMLStreamReader(
        //        new FileInputStream("/home/tdh/tmp/xqueryx-1.xml"));
        XMLStreamReader parser = factory.createXMLStreamReader(
                new ByteArrayInputStream(xqueryx_str.getBytes()));

        ArrayList<String> lpathArray = new ArrayList<String>();
        StringBuilder lpath_expression = new StringBuilder();
        Stack<Pair<String,Integer>> exprStacks = new Stack<Pair<String,Integer>>();
        //exprStacks.push(new Pair<String,String>("",Integer.valueOf(path_depth)));
        //Stack<String> exprNameStack = new Stack<String>();
        //exprNameStack.push("");
        StringBuilder positionSelector = new StringBuilder();
        Stack<String> xqueryAncestorStack = new Stack<String>();
        boolean no_arg_paren=false;
        boolean no_arg_paren_parent=false;
        boolean did_append=false;
        boolean axis_is_attr=false;
        boolean in_last=false;
        String grandchild="";
        String child="";
        String current="";
        String lastended="";
        String nextlastended="";
        String nextnextlastended="";
        String nextnextnextlastended="";
        String parent="";
        String grandparent="";
        String greatgrandparent="";
        String last_axis="";
        int last_pathexpr_depth=0;

        int event = parser.nextTag();
        int path_depth=0;
        int popped_depth=0;
        while (true) {
            if (event == XMLStreamConstants.END_DOCUMENT) {
                parser.close();
                break;
            }
            switch (event) {
                case XMLStreamConstants.START_ELEMENT:
                    //path_depth++;
                    //String tagName = formatTag(parser.getName());
                    String tagName = parser.getName().getLocalPart();
                    //System.out.println(tagName + ": " + lpath_expression);
                    //if(build_stack) {
                    //System.out.println(tagName + " push");
                    //exprStacks.push(new Stack<String>());
                    //exprNameStack.push(tagName);
                    //}
                    greatgrandparent = grandparent;
                    grandparent = parent;
                    parent = xqueryAncestorStack.isEmpty() ? "" : xqueryAncestorStack.peek();
                    xqueryAncestorStack.push(tagName);
                    if( tagName.equals( "stepExpr" ) ) {
                    //    if(lastended.equals("stepExpr")) {
                     //       exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                    //    }
                        did_append=false;
                    } else if( tagName.equals( "pathExpr" ) ) {
                        //path_depth++;
                        //build_stack = false;
                        //last_pathexpr_depth=path_depth;
                            exprStacks.push((new Pair<String,Integer>("",Integer.valueOf(-1))));
                    } else if( tagName.equals( "predicates" ) ) {
                        //if(!(lastended.equals("argExpr") && nextlastended.equals("varRef"))) {// for $hotelnode[good]/location
                           //if(!parent.equals("stepExpr")) {
                               exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                           //}
                           did_append=false;
                        //}
                    } else if( tagName.equals( "rootExpr" ) ) { // for / or //
                        /*if(lpathArray.get(lpathArray.size()-1)==LEFT_PAREN) {
                            lpathArray.remove(lpathArray.size()-1);
                            lpath_expression.deleteCharAt(lpath_expression.length()-1);
                            exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                            no_arg_paren=true;
                        }
                        if(greatgrandparent.equals("argExpr")) {
                            lpathArray.remove(lpathArray.size()-1);
                            lpath_expression.deleteCharAt(lpath_expression.length()-1);
                            exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                            no_arg_paren_parent=true;
                        } */
                        exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                    } else if( tagName.equals( "andOp" ) ||
                            tagName.equals( "orOp"  ) ||
                            tagName.equals( "lessThanOrEqualOp"  ) ||
                            tagName.equals( "greaterThanOrEqualOp"  )  ) {
                        xqueryAncestorStack.pop();
                        //if(xqueryAncestorStack.peek().equals("predicates")) {
                        lpath_expression.append(LEFT_PAREN);
                        lpathArray.add(LEFT_PAREN);
                        //xpathList.add(LEFT_PAREN);
                        //}
                        xqueryAncestorStack.push(tagName);
                    } else if(tagName.equals("addOp")) {
                            lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        }  else if(tagName.equals("subtractOp")) {
                            lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        }  else if(tagName.equals("multiplyOp")) {
                        lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        }  else if(tagName.equals("divOp")) {
                        lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        }  else if(tagName.equals("modOp")) {
                        lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        } else if(tagName.equals("unaryMinusOp")) {
                            lpathArray.add(LEFT_PAREN);
                            lpathArray.add(MINUS);
                            lpath_expression.append(LEFT_PAREN).append(MINUS);
                        } else if( tagName.equals( "functionName" ) ) {
                        event = parser.next();
                        if(event==XMLStreamConstants.CHARACTERS) {
                            String name = parser.getText();
                            if(name!=null) {
                                if(name.equals("position")) {
                                    //exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                                    String lpath = getPath(exprStacks);
                                    lpath_expression.append(lpath).append(" ");
                                    lpathArray.add(lpath);
                                    lpathArray.add(" ");
                                }
                                if(name.equals("last")) {
                                    if(!greatgrandparent.equals("stepExpr")) {in_last=true;} // for //a/last()/b
                                    path_depth++;
                                    if(lastended.equals("stepExpr")) {
                                    exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                                    }
                                    exprStacks.push((new Pair<String,Integer>(LAST,Integer.valueOf(path_depth))));
                                }
                                lpath_expression.append(name).append(LEFT_PAREN);
                                lpathArray.add(name);
                                lpathArray.add(LEFT_PAREN);
                            }
                        } else {
                            continue;
                        }
                    //} else if( tagName.equals( "stepExpr" ) ) {
                        //if(!parent.equals("pathExpr")
                        //exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                     } else if( tagName.equals( "anyKindTest" ) ) {
                        if(!last_axis.equals(DESCENDANT_OR_SELF)                        // for //a/b
                                && !last_axis.equals(PARENT)                            // for a/b/../c
                        ) {               
                                    //lpath_expression.append(lpath).append(SLASH);
                                    //lpathArray.add(lpath);
                                    //lpathArray.add(SLASH);
                                    path_depth++;
                                    if(lastended.equals("stepExpr")) {
                                        exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                                    }
                                exprStacks.push((new Pair<String,Integer>(NODE,Integer.valueOf(path_depth))));
                                //lpath_expression.append("node").append(LEFT_PAREN);
                                //lpathArray.add("node");
                                //lpathArray.add(LEFT_PAREN);
                    }
                    } else if ( tagName.equals( "textTest" ) ) {
                          //lpath_expression.append("text").append(LEFT_PAREN).append(RIGHT_PAREN);
                          //      lpathArray.add("text");
                          //      lpathArray.add(LEFT_PAREN);
                          //      lpathArray.add(RIGHT_PAREN);
                                    //exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                                    exprStacks.push((new Pair<String,Integer>(TEXT,Integer.valueOf(path_depth))));
                    } else if ( tagName.equals( "commentTest" )  ) {
                           //lpath_expression.append("comment").append(LEFT_PAREN).append(RIGHT_PAREN);
                           //     lpathArray.add("comment");
                           //     lpathArray.add(LEFT_PAREN);
                           //     lpathArray.add(RIGHT_PAREN);
                                    path_depth++;
                           //        exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                                    exprStacks.push((new Pair<String,Integer>(COMMENT,Integer.valueOf(path_depth))));
                     } else if( tagName.equals( "filterExpr" ) ) {
                        //exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                     } else if( tagName.equals( "xpathAxis" ) ) {
                        if(   (
                                 lastended.equals("stepExpr")                         // for a/b
                                 || lastended.equals("firstOperand")           // for a/b/(c | d)
                              //|| nextlastended.equals("stepExpr")            // for a/b[c[d]]
                              //|| lastended.equals("filterExpr")              // for (a)/b
                              //|| greatgrandparent.equals("firstOperand")     // for a/b[c[d] or e]
                              //|| greatgrandparent.equals("secondOperand")
                              ) //&& (
                                //!greatgrandparent.equals("predicates")       // for a[b]
                                //)    // for a/b[e or c[d]]
                        ) {
                        //if(!grandparent.equals("pathExpr")) {
                            exprStacks.push((new Pair<String,Integer>(SLASH,Integer.valueOf(path_depth))));
                            //xpathList.add(SLASH);
                        }
                        axis_is_attr=false;
                        event = parser.next();
                        if(event==XMLStreamConstants.CHARACTERS) {
                            //while(exprStacks.size()>0 && exprStacks.peek().getSecond()>path_depth-1) {
                            //    exprStacks.pop();
                            //}
                            last_axis = parser.getText();
                            if(last_axis.equals(ATTRIBUTE)) {
                                exprStacks.push((new Pair<String,Integer>(AMPERSAND,Integer.valueOf(path_depth))));
                                axis_is_attr=true;
                                /*event = parser.nextTag(); // end
                                event = parser.nextTag();
                                path_depth++;
                                tagName = parser.getName().getLocalPart();
                                if(event==XMLStreamConstants.START_ELEMENT) {
                                    xqueryAncestorStack.push(tagName);
                                    if( tagName.equals( "nameTest" ) ) {
                                        event = parser.next();
                                        if(event==XMLStreamConstants.CHARACTERS) {
                                            String name = parser.getText();
                                            if(name!=null) {

                                            }
                                        }
                                    }
                                }*/
                            } else if(last_axis.equals(ANCESTOR)) {
                                exprStacks.push((new Pair<String,Integer>(ANCESTOR,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(ANCESTOR);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(ANCESTOR_OR_SELF)) {
                                exprStacks.push((new Pair<String,Integer>(ANCESTOR_OR_SELF,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(ANCESTOR_OR_SELF);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(DESCENDANT)) {
                                exprStacks.push((new Pair<String,Integer>(DESCENDANT,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(DESCENDANT);
                                //xpathList.add(TWOCOLONS);
                                //if(exprStacks.peek().getFirst()==LEFT_PAREN) {
                                //    exprStacks.pop();
                                //    exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                                //    no_arg_paren=true;
                                //}
                            } else if(last_axis.equals(DESCENDANT_OR_SELF)) {
                                //exprStacks.push((new Pair<String,Integer>(DESCENDANT_OR_SELF,Integer.valueOf(path_depth))));
                                //exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(DESCENDANT_OR_SELF);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(FOLLOWING)) {
                                exprStacks.push((new Pair<String,Integer>(FOLLOWING,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(FOLLOWING);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(FOLLOWING_SIBLING)) {
                                exprStacks.push((new Pair<String,Integer>(FOLLOWING_SIBLING,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(FOLLOWING_SIBLING);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(NAMESPACE)) {
                                exprStacks.push((new Pair<String,Integer>(NAMESPACE,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(NAMESPACE);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(PARENT)) {
                                //exprStacks.push((new Pair<String,Integer>(PARENT,Integer.valueOf(path_depth))));
                                //exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWODOTS,Integer.valueOf(path_depth))));
                                //xpathList.add(PARENT);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(PRECEDING)) {
                                exprStacks.push((new Pair<String,Integer>(PRECEDING,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(PRECEDING);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(PRECEDING_SIBLING)) {
                                exprStacks.push((new Pair<String,Integer>(PRECEDING_SIBLING,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(PRECEDING_SIBLING);
                                //xpathList.add(TWOCOLONS);
                            } else if(last_axis.equals(SELF)) {
                                exprStacks.push((new Pair<String,Integer>(SELF,Integer.valueOf(path_depth))));
                                exprStacks.push((new Pair<String,Integer>(TWOCOLONS,Integer.valueOf(path_depth))));
                                //xpathList.add(SELF);
                                //xpathList.add(TWOCOLONS);
                            }
                        } else {
                            continue;
                        }
                    } else if( tagName.equals( "nameTest" ) ) {
                        event = parser.next();
                        if(event==XMLStreamConstants.CHARACTERS) {
                            //while(exprStacks.size()>0 && exprStacks.peek().getSecond()>path_depth-1) {
                            //    exprStacks.pop();
                            //}
                            String name = parser.getText();
                            if(name!=null) {
                                path_depth++;
                                exprStacks.push((new Pair<String,Integer>(name,Integer.valueOf(path_depth))));
                                /*if(axis_is_attr) {
                                    exprStacks.push((new Pair<String,Integer>(name,Integer.valueOf(path_depth))));
                                    String lpath = getPath(exprStacks);
                                    lpath_expression.append(lpath);
                                    lpathArray.add(lpath);
                                    while(!exprStacks.peek().getSecond().equals("pathExpr")) {
                                        if(exprStacks.size()==1) {break;}
                                        exprStacks.pop();
                                    }
                                    exprStacks.pop();
                                    did_append=true;
                                }*/
                                //xpathList.add(name);
                            }
                        } else {
                            continue;
                        }
                    } else if( tagName.equals( "integerConstantExpr" ) ) {
                        event = parser.nextTag();
                        tagName = parser.getName().getLocalPart();
                        if(event==XMLStreamConstants.START_ELEMENT) {
                            xqueryAncestorStack.push(tagName);
                            if( tagName.equals( "value" ) ) {
                                event = parser.next();
                                if(event==XMLStreamConstants.CHARACTERS) {
                                    String name = parser.getText();
                                    if(name!=null) {
                                        //if (!parent.equals("predicates")) {
                                            lpath_expression.append(name);
                                            lpathArray.add(name);
                                        /*} else {
                                            if(grandparent.equals("stepExpr")) {
                                                if(exprStacks.peek().getFirst()==SLASH) {
                                                    exprStacks.pop();
                                                }
                                                exprStacks.push((new Pair<String,String>(LEFT_BRACKET+name+RIGHT_BRACKET,tagName)));
                                            } else {
                                            lpath_expression.append(LEFT_BRACKET).append(name).append(RIGHT_BRACKET);
                                            lpathArray.add(LEFT_BRACKET);
                                            lpathArray.add(name);
                                            lpathArray.add(LEFT_BRACKET);
                                            }
                                        } */
                                       // if (exprStacks.peek().getFirst()!=SLASH) {
                                       //     lpath_expression.append(name);
                                       ///     lpathArray.add(name);
                                       // } else {
                                        //    exprStacks.pop();
                                        //    if (exprStacks.peek().getFirst()==SLASH) {
                                       //         exprStacks.pop();
                                        //    }
                                       ///     //if(lpath_expression.charAt(lpath_expression.length()-1)!=')') {
                                         //   //    lpath_expression.append(RIGHT_PAREN);
                                         //   //    lpathArray.add(RIGHT_PAREN);
                                        //    //}
                                        //    //exprStacks.push((new Pair<String,String>(LEFT_BRACKET,tagName)));
                                       //     //exprStacks.push((new Pair<String,String>(name,tagName)));
                                        //    //exprStacks.push((new Pair<String,String>(RIGHT_BRACKET,tagName)));
                                        //    lpath_expression.append(LEFT_BRACKET).append(name).append(RIGHT_BRACKET);
                                        //    lpathArray.add(LEFT_BRACKET);
                                        //    lpathArray.add(name);
                                        //    lpathArray.add(LEFT_BRACKET);
                                       //}
                                    }
                                }
                            } else {
                                continue;
                            }
                        }
                    } else if( tagName.equals( "stringConstantExpr" ) ) {
                        event = parser.nextTag();
                        tagName = parser.getName().getLocalPart();
                        if(event==XMLStreamConstants.START_ELEMENT) {
                            xqueryAncestorStack.push(tagName);
                            if( tagName.equals( "value" ) ) {
                                event = parser.next();
                                if(event==XMLStreamConstants.CHARACTERS) {
                                    String name = parser.getText();
                                    if(name!=null) {
                                        lpath_expression.append("'" + name + "'");
                                        lpathArray.add("'" + name + "'");
                                    }
                                }
                            } else {
                                continue;
                            }
                        }
                    } else if( tagName.equals( "varRef" ) ) {
                        //lpath_expression.append("$");
                        //lpathArray.add("$");
                        event = parser.nextTag();
                        tagName = parser.getName().getLocalPart();
                        if(event==XMLStreamConstants.START_ELEMENT) {
                            xqueryAncestorStack.push(tagName);
                            if( tagName.equals( "name" ) ) {
                                event = parser.next();
                                if(event==XMLStreamConstants.CHARACTERS) {
                                    String name = parser.getText();
                                    if(name!=null) {
                                        ///if((parent.equals("firstOperand") || parent.equals("secondOperand")) ) {
                                          lpath_expression.append("$").append(name);
                                          lpathArray.add("$"+name);
                                        /*} else {
                                            //exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                                             exprStacks.push((new Pair<String,String>("$"+name,tagName)));
                                        }
                                        //if(parent.equals("argExpr") && greatgrandparent.equals("queryBody")) {
                                        if(parent.equals("argExpr") && grandparent.equals("pathExpr")) {
                                           lpathArray.remove(lpathArray.size()-1);
                                           lpath_expression.deleteCharAt(lpath_expression.length()-1);
                                           exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                                           no_arg_paren=true;   
                                        }  */
                                        //xpathList.add("$" + name);
                                    }
                                }
                            } else {
                                continue;
                            }
                        }
                    } else if(tagName.equals("argExpr")) {
                                //    exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                        //if(parent.equals("pathExpr")) {
                            lpath_expression.append(LEFT_PAREN);
                            lpathArray.add(LEFT_PAREN);
                        //}
                               }
                    break;
                case XMLStreamConstants.END_ELEMENT:
                    //path_depth--;
                    //for (String eltname : xqueryAncestorStack) System.err.print(eltname+SLASH); System.err.print("\n");
                    grandchild = child;
                    child = current;
                    current = xqueryAncestorStack.isEmpty() ? "" : xqueryAncestorStack.pop();
                    nextnextnextlastended = nextnextlastended;
                    nextnextlastended = nextlastended;
                    nextlastended = lastended;
                    lastended = current;
                    parent  = xqueryAncestorStack.isEmpty() ? "" : xqueryAncestorStack.peek();
                    tagName = parser.getName().getLocalPart();
                    //System.err.println("-" + tagName);
                    if( tagName.equals( "pathExprXXX" ) ) {
                        //build_stack = true;
                        //if(!parent.equals("argExpr")) {
                        if(!(no_arg_paren || no_arg_paren_parent)) {
                            //if(!parent.equals("queryBody")) {
                            if(!child.equals("predicates") && !grandchild.equals("predicates")) {
                                String lpath = getPath(exprStacks);                                
                                if(lpath_expression.length()==0) {
                                    //lpath_expression.append(LEFT_PAREN);
                                    lpath_expression.append(lpath);
                                } else {//if(!(lpath_expression.charAt(lpath_expression.length()-1)==')')) {
                                    //lpath_expression.append(RIGHT_PAREN);
                                    //lpath_expression.append(LEFT_PAREN);
                                //} else {
                                    //lpath_expression.append(LEFT_PAREN);
                                    lpath_expression.append(" ");
                                    lpath_expression.append(lpath);
                                    //lpath_expression.append(RIGHT_PAREN);
                                }
                                lpathArray.add(lpath);
                                //if(parent.equals("firstOperand")) {
                                //    lpath_expression.append(COMMA);
                                //    lpathArray.add
                                //}
                                if( grandchild.equals( "nodeTest" ) ) {
                                    lpath_expression.append(NODE);
                                    lpathArray.add(NODE);
                                } else if( grandchild.equals( "textTest" ) ) {
                                    lpath_expression.append(TEXT);
                                    lpathArray.add(TEXT);
                                } else if( grandchild.equals( "commentTest" ) ) {
                                    lpath_expression.append(COMMENT);
                                    lpathArray.add(COMMENT);
                                }
                            }
                        //} else {
                           // boolean test=true;
                         //   if(test) test=!test;
                        //}
                        //if(!parent.equals("argExpr")) {
                            if(!(no_arg_paren || no_arg_paren_parent) && !(parent.equals("argExpr"))) {
                            //System.out.println(tagName + " pop: " + exprStacks.peek().getSecond());
                            while(!exprStacks.peek().getSecond().equals("pathExpr")) {
                                if(exprStacks.size()==1) {break;}
                                exprStacks.pop();
                                //exprNameStack.pop();
                                //System.out.println(tagName + " pop: " + exprStacks.peek().getSecond());
                            }
                            exprStacks.pop();
                            }
                        }
                    } else if( tagName.equals( "stepExprXXX" ) ) {
                        String lpath = getPath(exprStacks);
                        lpath_expression.append(lpath);
                        lpathArray.add(lpath);
                        while(!exprStacks.peek().getSecond().equals("pathExpr")) {
                                if(exprStacks.size()==1) {break;}
                                exprStacks.pop();
                        }
                        exprStacks.pop();
                        did_append=true;
                    } else if( tagName.equals( "predicates" ) ) {
                        if(exprStacks.size()>0 && exprStacks.peek().getFirst()==SLASH) {
                            exprStacks.pop();
                        }
                    } else if( tagName.equals( "pathExpr" ) ) {

                        //exprStacks.push((new Pair<String,String>(TWODOTS,tagName)));
                        //if(!did_append) {
                            //exprStacks.push((new Pair<String,String>(" ","spacer")));
                        //if(!parent.equals("firstOperand") && // for  a/b[c[d] or e]
                        //   !parent.equals("secondOperand") &&
                        //   !parent.equals("arguments")  ) {
                        if(                  !parent.equals("sequenceExpr")     // for (a/c)/b 
                          // && !nextnextnextlastended.equals("functionCallExpr") // for a/last()
                        ) {
                        if(path_depth!=popped_depth ) {
                            String lpath = getPath(exprStacks);
                            lpath_expression.append(" ").append(lpath);
                            lpathArray.add(" ");
                            lpathArray.add(lpath);                            
                        }
                        path_depth--;
                        while(exprStacks.size()>0 && exprStacks.peek().getSecond()>path_depth) {
                            exprStacks.pop();
                        }
                        popped_depth = path_depth;
                        while(exprStacks.size()>0 && exprStacks.peek().getSecond()!=-1) {
                            exprStacks.pop();
                        }
                        if(exprStacks.size()>0) {
                            exprStacks.pop();
                        }
                    }
                        //    did_append=true;
                        //}
                    } else if( tagName.equals( "andOp" ) ||
                            tagName.equals( "orOp"  ) ||
                            tagName.equals( "lessThanOrEqualOp"  ) ||
                            tagName.equals( "greaterThanOrEqualOp"  )  ) {
                        //if(parent.equals("predicates")) {
                        lpath_expression.append(RIGHT_PAREN);
                        lpathArray.add(RIGHT_PAREN);
                        //xpathList.add(RIGHT_PAREN);

                        //}
                    } else if(tagName.equals("addOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("subtractOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("multiplyOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("divOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("modOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        } else if( tagName.equals( "nodeTest" ) ) {
                    } else if( tagName.equals( "textTest" ) ) {
                    } else if( tagName.equals( "commentTest" ) ) {
                    } else if( tagName.equals( "firstOperand" ) ) {
                        if(parent.equals("andOp")) {
                            lpath_expression.append(AND);
                            lpathArray.add(AND);
                        } else if(parent.equals("orOp")) {
                            lpath_expression.append(OR);
                            lpathArray.add(OR);
                        }  else if(parent.equals("equalOp")) {
                            lpath_expression.append(EQUALS);
                            lpathArray.add(EQUALS);
                        }  else if(parent.equals("NotEqualOp")) {
                            lpath_expression.append(NOT_EQUALS);
                            lpathArray.add(NOT_EQUALS);
                        }  else if(parent.equals("greaterThanOp")) {
                            lpath_expression.append(GT);
                            lpathArray.add(GT);
                        }  else if(parent.equals("greaterThanOrEqualOp")) {
                            lpath_expression.append(GTE);
                            lpathArray.add(GTE);
                        }  else if(parent.equals("lessThanOp")) {
                            lpath_expression.append(LT);
                            lpathArray.add(LT);
                        }  else if(parent.equals("lessThanOrEqualOp")) {
                            lpath_expression.append(LTE);
                            lpathArray.add(LTE);
                        }  else if(parent.equals("addOp")) {
                            lpath_expression.append(PLUS);
                            lpathArray.add(PLUS);
                        }  else if(parent.equals("subtractOp")) {
                            lpath_expression.append(MINUS);
                            lpathArray.add(MINUS);
                        }  else if(parent.equals("multiplyOp")) {
                            lpath_expression.append(MULT);
                            lpathArray.add(MULT);
                        }  else if(parent.equals("divOp")) {
                            lpath_expression.append(DIV);
                            lpathArray.add(DIV);
                        }  else if(parent.equals("modOp")) {
                            lpath_expression.append(MOD);
                            lpathArray.add(MOD);
                        } else if(parent.equals("unionOp")) {
                            lpath_expression.append(UNION);
                            lpathArray.add(UNION);
                        }
                    } else if(tagName.equals("addOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("subtractOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("multiplyOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("divOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        }  else if(tagName.equals("modOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        } else if(tagName.equals("unaryMinusOp")) {
                        lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        } else if( tagName.equals( "firstOperand" ) ||
                            ( tagName.equals( "secondOperand" )  )) {
                        //lpath_expression.append(RIGHT_PAREN);
                        //lpathArray.add(RIGHT_PAREN);
                    } else if( tagName.equals( "functionCallExpr" ) ) {
                        if(in_last) {      // for /a/b[last() and i]/f
                        if(path_depth!=popped_depth ) {
                            String lpath = getPath(exprStacks);
                            lpath_expression.append(" ").append(lpath);
                            lpathArray.add(" ");
                            lpathArray.add(lpath);
                        }
                        path_depth--;
                        while(exprStacks.size()>0 && exprStacks.peek().getSecond()>path_depth) {
                            exprStacks.pop();
                        }
                        popped_depth = path_depth;
                        /*while(exprStacks.size()>0 && exprStacks.peek().getSecond()!=-1) {
                            exprStacks.pop();
                        }
                        if(exprStacks.size()>0) {
                            exprStacks.pop();
                        }*/
                        in_last=false;
                        }
                    } else if( tagName.equals( "arguments" ) ) {
                        lpath_expression.append(RIGHT_PAREN);
                        lpathArray.add(RIGHT_PAREN);
                        // Treating "last() as node, not function
                        if(lpath_expression.length()>5 && lpath_expression.substring(lpath_expression.length()-6, lpath_expression.length()).equals(LAST)) {
                             lpath_expression.delete(lpath_expression.length()-6, lpath_expression.length());
                             lpathArray.remove(lpathArray.size()-1);
                             lpathArray.remove(lpathArray.size()-1);
                             lpathArray.remove(lpathArray.size()-1);
                        }
                    //}  else if( tagName.equals( "varRef" ) ) {
                    //    exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                    } else if( tagName.equals( "argExpr" ) ) {
                        //exprStacks.push((new Pair<String,String>(SLASH,tagName)));
                        /*if(parent.equals("pathExpr")) {
                            if(xpathList.size()>0 && xpathList.get(xpathList.size()-1).equals(LEFT_PAREN)) {
                               xpathList.set((xpathList.size()-1), SLASH);
                            } else if(xpathList.size()>0 && xpathList.get(xpathList.size()-1).equals(SLASH)) {
                                if(xpathList.size()>1 && xpathList.get(xpathList.size()-2).equals(LEFT_PAREN)) {
                                   xpathList.set((xpathList.size()-2), SLASH);
                                }
                            } else if(child.equals("varRef") && xpathList.size()>1 && xpathList.get(xpathList.size()-2).equals(LEFT_PAREN)) {
                                xpathList.set((xpathList.size()-2), "");
                                xpathList.add(SLASH);
                            } else {
                               xpathList.add(RIGHT_PAREN);
                            }
                        } else {
                           xpathList.add(SLASH);
                        }    */
                        /*if(parent.equals("pathExpr")) {
                            if(lpathArray.size()>0 && lpathArray.get(lpathArray.size()-1).equals(LEFT_PAREN)) {
                                lpath_expression.append(SLASH);
                               lpathArray.set((lpathArray.size()-1), SLASH);
                            } else if(lpathArray.size()>0 && lpathArray.get(lpathArray.size()-1).equals(SLASH)) {
                                if(lpathArray.size()>1 && lpathArray.get(lpathArray.size()-2).equals(LEFT_PAREN)) {
                                    lpath_expression.append(SLASH);
                                   lpathArray.set((lpathArray.size()-2), SLASH);
                                }
                            } else if(child.equals("varRef") && lpathArray.size()>1 && lpathArray.get(lpathArray.size()-2).equals(LEFT_PAREN)) {
                                lpathArray.set((lpathArray.size()-2), "");
                                lpath_expression.append(SLASH);
                            lpathArray.add(SLASH);
                            } else {
                                lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                            }
                        } else {
                            lpath_expression.append(SLASH);
                            lpathArray.add(SLASH);
                        }*/
                        //if(parent.equals("pathExpr")) {
                            /*if(no_arg_paren) {
                                no_arg_paren=false;
                            } else if(no_arg_paren_parent) {
                                no_arg_paren_parent=false;
                            } else {
                                lpath_expression.append(RIGHT_PAREN);
                                lpathArray.add(RIGHT_PAREN);
                            }*/
                            lpath_expression.append(RIGHT_PAREN);
                            lpathArray.add(RIGHT_PAREN);
                        //}
                    }
                    //if(build_stack) {
                    //    System.out.println(tagName + " pop: " + exprNameStack.peek());
                    //    exprStacks.pop();
                    //    exprNameStack.pop();
                    //}
                    //if( tagName.equals( "pathExpr" ) ) {
                        //build_stack = true;
                    //}
                    break;
                //case XMLStreamConstants.CHARACTERS:
                //    String characters = parser.getText().trim();
                //    if(characters.equals(RIGHT_PAREN)) {
                //        xpathList.add(RIGHT_PAREN);
                //    } else if(characters.equals(LEFT_PAREN)) {
                //        xpathList.add(LEFT_PAREN);
                //    }
                //    break;
                default:
                    break;
            }

            if(!parser.hasNext()) {
                parser.close();
                break;
            }
            event = parser.next();
        }

        System.err.println(lpath_expression.toString());

        return lpathArray;
    }


    public List<Pair<String, Boolean>> combineOperations(List<String> lpathList) {
        ArrayList<Pair<String, Boolean>> combined = new ArrayList<Pair<String, Boolean>>();
        ListIterator<String> lpathListItr = lpathList.listIterator(0);
        // Find paths that are first operand of a literal comparison
        while(lpathListItr.hasNext()) {
            Boolean isCombination=Boolean.FALSE;
            String fullpath = lpathListItr.next();
            if(fullpath.charAt(0)=='/') {
                if(lpathListItr.hasNext()) {
                    String op = lpathListItr.next();
                    if("=".equals(op)  ||
                            "!=".equals(op) ||
                            ">".equals(op)  ||
                            ">=".equals(op) ||
                            "<".equals(op)  ||
                            "<=".equals(op)  ) {
                        if(lpathListItr.hasNext()) {
                            String literal = lpathListItr.next();
                            if(literal.charAt(0)=='/') {
                                lpathListItr.previous();
                                lpathListItr.previous();
                            } else {
                                if(literal.startsWith("'") || literal.startsWith("$")) {
                                    fullpath = fullpath + op + literal;
                                    isCombination=Boolean.TRUE;
                                } else {
                                    boolean decodeable=true;
                                    try {
                                        Double.parseDouble(literal);
                                    } catch (NumberFormatException nfe) {
                                        decodeable=false;
                                    }
                                    if(decodeable) {
                                        fullpath = fullpath + op + literal;
                                        isCombination=Boolean.TRUE;
                                    } else {
                                        lpathListItr.previous();
                                        lpathListItr.previous();
                                    }
                                }
                            }
                        }
                    }
                }
            }
            combined.add(new Pair<String, Boolean>(fullpath, isCombination));
            //System.out.println(fullpath);
        }
        return combined;
    }

    public ArrayList<String> combineBooleans(List<Pair<String, Boolean>> combined) {
        ArrayList<String> boolcomb = new ArrayList<String>();
        ListIterator<Pair<String, Boolean>> inItr = combined.listIterator(0);
        // Find paths that are first operands of a boolean
        while(inItr.hasNext()) {
            StringBuilder combpath = new StringBuilder();
            Pair<String, Boolean> pathpair = inItr.next();
            combpath.append(pathpair.getFirst());
            if(pathpair.getSecond()) {
                if(inItr.hasNext()) {
                    Pair<String, Boolean> oppair = inItr.next();
                    String op = oppair.getFirst();
                    if(" and ".equals(op) || " or ".equals(op)) {
                        while(inItr.hasNext()) {
                            Pair<String, Boolean> nextpathpair = inItr.next();
                            if(nextpathpair.getSecond()) {
                                combpath.append(op).append(nextpathpair.getFirst());
                            } else {
                                inItr.previous();
                                inItr.previous();
                                break;
                            }
                            if(inItr.hasNext()) {
                                Pair<String, Boolean> nextoppair = inItr.next();
                                String nextop = nextoppair.getFirst();
                                if(!nextop.equals(op)) {
                                    inItr.previous();
                                    break;
                                }
                            }
                        }
                    } else {
                        inItr.previous();
                    }
                }
            }
            boolcomb.add(combpath.toString());
        }
        return boolcomb;
    }

    /** Iterate through the stacks, producing the lpath.
     *
     */
    public String getPath(Stack<Pair<String,Integer>> stringStacks) {
        StringBuilder lpath=new StringBuilder();
        String previous=null;
        for(Pair<String,Integer> eltpair :  stringStacks) {
            String nodename = eltpair.getFirst();
            if(nodename==TWODOTS) {
                if(previous==SLASH) {
                    lpath.deleteCharAt(lpath.length()-1);
                    if(lpath.length()>0)
                    lpath.delete(lpath.lastIndexOf(SLASH), lpath.length());
                }
                previous=nodename;
                continue;
            }
            lpath.append(nodename);
            previous=nodename;
        }

        return lpath.toString();
    }

    public List<String> getFullPaths(List<String> pathArray) {
        List<String> lFullPaths = new ArrayList<String>();

        for(String listentry : pathArray) {
            if((listentry.charAt(0)=='/')) {
                lFullPaths.add(listentry);
            }
        }

        return lFullPaths;
    }

    /**
     * Format the XML tag. Takes as input the QName of the tag, and formats
     * it to a namespace:tagname format.
     * @param qname the QName for the tag.
     * @return the formatted QName for the tag.
     */
    //private String formatTag(QName qname) {
    //    String prefix = qname.getPrefix();
    //    String suffix = qname.getLocalPart();
    //    if (StringUtils.isBlank(prefix)) {
    //        return suffix;
    //    } else {
    //        return StringUtils.join(new String[] {prefix, suffix}, ":");
    //    }
    //}

}